from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.edge.options import Options
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.edge.service import Service
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import pymysql
import random
import time
from datetime import datetime

# -------------------------- 基础配置参数 --------------------------
# --- 浏览器配置 ---
EDGE_BINARY_PATH = r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
EDGE_DRIVER_PATH = r"G:\python实战\driver\msedgedriver.exe"
INITIAL_MANUAL_OPERATE_TIME = 50  # 首次手动操作时间（秒）
MIN_WAIT_TIME = 1
MAX_WAIT_TIME = 5
VERIFY_CODE_SLEEP_TIME = 25  # 验证码处理休眠时间（秒）
RETURN_LIST_WAIT_TIME = 5  # 返回关注列表的等待时间（秒）

# --- 数据库配置 ---
DB_CONFIG = {
    'host': 'localhost',
    'port': 3306,
    'user': 'root',  # 请修改为你的 MySQL 用户名
    'password': 'DHP041011',  # 请修改为你的 MySQL 密码
    'database': 'douyin_bot',  # 请修改为你的数据库名
    'charset': 'utf8mb4'
}

# --- 评论任务配置 ---
# 为第二组准备带占位符的评论模板
COMMENT_TEMPLATES_GROUP2 = [
    "坚持打卡第{days}天 现在是{time}，求一个水果手机每天坚持打卡~直到被领到！！！",
    "打卡第{days}天 现在是北京时间：{time}，只求水果手机，还没领到，坚持打卡！",
    "坚持打卡第{days}天 现在是{time}，求一个苹果手机，坚持打卡打卡，每天坚持打卡求求~",
    "坚持打卡第{days}天 现在是北京时间：{time}，求一个水果手机，坚持打卡直到领到为止~",
    "坚持就是胜利，打卡第{days}天 现在是北京时间：{time}，求一个水果手机~"
]
COMMENT_TEMPLATES_GROUP3 = []
TASKS = [
    {
        'table_name': 'bloggers_group1',
        'comment_contents': [],  # 空列表表示发送随机数字
        'comment_count': 4
    },
    {
        'table_name': 'bloggers_group2',
        'comment_contents': COMMENT_TEMPLATES_GROUP2,  # 使用带占位符的模板
        'comment_count': 2
    },
    {
        'table_name': 'bloggers_group3',  # 第三组的数据库表名
        'comment_contents': COMMENT_TEMPLATES_GROUP3,  # 0-30的随机数字
        'comment_count': 4  # 可自定义该组的评论数量（示例：3条）
    }
]

# 全局变量，用于跟踪本次运行的总评论数
current_run_total = 0
# 本次运行的记录ID，用于后续更新
current_run_id = None


# 新增：定义自定义异常
class SkipBloggerException(Exception):
    """跳过当前博主的异常"""
    pass


# --------------------------------------------------------------------------------

# ========================== 数据库操作函数 ==========================
def get_db_connection():
    """建立并返回数据库连接"""
    try:
        connection = pymysql.connect(**DB_CONFIG)
        return connection
    except pymysql.MySQLError as e:
        print(f"❌ 数据库连接失败: {e}")
        exit()


def get_bloggers_from_table(table_name):
    """从指定数据表中获取所有博主信息"""
    connection = None
    bloggers = []
    try:
        connection = get_db_connection()
        with connection.cursor(pymysql.cursors.DictCursor) as cursor:
            sql = f"SELECT id, name, last_operate_time, send_days, total_send_count FROM {table_name};"
            cursor.execute(sql)
            bloggers = cursor.fetchall()
    except pymysql.MySQLError as e:
        print(f"❌ 从表 {table_name} 查询博主失败: {e}")
    finally:
        if connection:
            connection.close()
    return bloggers


def update_blogger_statistics(table_name, blogger_id, current_send_count):
    """更新博主的统计信息"""
    connection = None
    try:
        connection = get_db_connection()
        with connection.cursor() as cursor:
            sql = f"""
                       UPDATE {table_name}
                       SET 
                           last_operate_time = CURRENT_TIMESTAMP,
                          send_days = IFNULL(send_days, 0) + IF(DATE(CURRENT_TIMESTAMP) != DATE(last_operate_time),1,0),
                           total_send_count = total_send_count + {current_send_count}
                       WHERE id = {blogger_id};
                       """
            cursor.execute(sql)
        connection.commit()
        print(f"✅ 数据库中博主ID {blogger_id} 的统计信息已更新。")
    except pymysql.MySQLError as e:
        print(f"❌ 更新博主ID {blogger_id} 的统计信息失败: {e}")
        if connection:
            connection.rollback()
    finally:
        if connection:
            connection.close()


# ========================== 新增的数据库操作函数 ==========================
def init_total_statistics():
    """
    初始化本次运行的统计记录，插入一条新记录
    """
    global current_run_id
    connection = None
    try:
        connection = get_db_connection()
        with connection.cursor() as cursor:
            # 插入一条新记录，初始数量为0
            insert_sql = """
            INSERT INTO send_total (last_operate_time, total_counts)
            VALUES (CURRENT_TIMESTAMP, 0);
            """
            cursor.execute(insert_sql)
            current_run_id = cursor.lastrowid  # 获取刚插入的记录ID
            print(f"✅ 已初始化本次运行的统计记录，记录ID: {current_run_id}")
        connection.commit()
        return current_run_id
    except pymysql.MySQLError as e:
        print(f"❌ 初始化统计记录失败: {e}")
        if connection:
            connection.rollback()
        return None
    finally:
        if connection:
            connection.close()


def update_total_statistics(additional_count):
    """
    更新本次运行的统计记录，累加评论数
    """
    global current_run_total, current_run_id
    current_run_total += additional_count  # 更新内存中的计数器

    # 如果还没有初始化统计记录，则先初始化
    if current_run_id is None:
        current_run_id = init_total_statistics()
        if current_run_id is None:
            return

    connection = None
    try:
        connection = get_db_connection()
        with connection.cursor() as cursor:
            # 更新本次运行的记录，累加评论数
            update_sql = """
            UPDATE send_total
            SET total_counts = total_counts + %s, last_operate_time = CURRENT_TIMESTAMP
            WHERE id = %s;
            """
            cursor.execute(update_sql, (additional_count, current_run_id))
            connection.commit()
            print(
                f"✅ 已更新统计记录 (ID: {current_run_id})，本次新增 {additional_count} 条评论，当前总计: {current_run_total} 条")
    except pymysql.MySQLError as e:
        print(f"❌ 更新统计记录失败: {e}")
        if connection:
            connection.rollback()
    finally:
        if connection:
            connection.close()


# ========================== 辅助函数 ==========================
def ask_skip_blogger(blogger_name):
    """询问用户是否要跳过当前博主"""
    while True:
        try:
            response = input(f"\n❓ 在关注列表中找不到博主 '{blogger_name}'，是否要跳过该博主？(y/n): ").strip().lower()
            if response in ['y', 'yes']:
                print(f"✅ 已选择跳过博主: {blogger_name}")
                return True
            elif response in ['n', 'no']:
                print(f"🔄 将继续尝试查找博主: {blogger_name}")
                return False
            else:
                print("⚠️  请输入有效的回答 (y/n)")
        except KeyboardInterrupt:
            print(f"\n⚠️  输入被中断，将继续尝试查找博主: {blogger_name}")
            return False


# ========================== Selenium 评论函数 ==========================
def get_current_uploader_name(driver, wait):
    """获取当前视频的上传者（博主）名称"""
    try:
        uploader_name = driver.execute_script("""
            var authorCard = document.querySelector('div.author-card-user-name');
            return authorCard ? authorCard.innerText.trim() : '';
        """)
        if uploader_name:
            return uploader_name
        print("⚠️  主方法获取博主名称失败，尝试备用 XPath 方案...")
        uploader_element = wait.until(
            EC.presence_of_element_located((By.XPATH, "//div[contains(@class, 'author-card-user-name')]")))
        return uploader_element.text.strip()
    except Exception as e:
        print(f"⚠️  无法获取当前视频的博主名称: {e}")
        return "未知博主"


def send_comments_to_blogger(driver, wait, blogger, comment_templates, comment_count, table_name):
    """向单个博主发送评论并更新统计"""
    blogger_name = blogger['name']
    blogger_id = blogger['id']
    processed = False

    while not processed:
        try:
            # 1. 定位并点击博主
            print(f"🔍 正在关注列表中查找博主：{blogger_name}...")
            try:
                blogger_element = wait.until(EC.element_to_be_clickable((By.XPATH,
                                                                         f"//li[contains(@class, 'pZz7hFIi')]//div[contains(@class, 'ILK2RAD5')]//span[text()='{blogger_name}']")))
                blogger_element.click()
                print(f"✅ 已点击进入博主 {blogger_name} 的页面")
                time.sleep(random.uniform(2, 3))
            except (TimeoutException, NoSuchElementException):
                print(f"❌ 在关注列表中找不到博主: {blogger_name}")
                if ask_skip_blogger(blogger_name):
                    raise SkipBloggerException(f"用户选择跳过博主: {blogger_name}")
                else:
                    print(f"⌛ 将在 {RETURN_LIST_WAIT_TIME} 秒后重试...")
                    time.sleep(RETURN_LIST_WAIT_TIME)
                    continue

            # 2. 等待手动点击视频
            print(f"\n⌛ 请在8秒内手动点击该博主的一个视频并打开评论区...")
            for i in range(8, 0, -1):
                print(f"⏳ 倒计时：{i}秒", end="\r")
                time.sleep(1)
            print(f"\n✅ 准备发送评论...")

            # 3. 发送评论
            get_current_uploader_name(driver, wait)

            success_count = 0
            current_attempt = 1

            # --- 核心改动：动态生成评论内容 ---
            last_op_time = blogger['last_operate_time']
            today = datetime.now().date()
            if last_op_time and last_op_time.date() == today:
                current_days = blogger['send_days']
            else:
                current_days = blogger['send_days'] + 1

            while success_count < comment_count:
                current_time = datetime.now().strftime("%m月%d日%H:%M")
                template = random.choice(comment_templates)

                if table_name == 'bloggers_group2':
                    comment_content = template.format(days=current_days, time=current_time)
                else:
                    comment_content = random.choice(comment_templates)

                try:
                    comment_input_div = wait.until(
                        EC.presence_of_element_located((By.CSS_SELECTOR, "div.public-DraftStyleDefault-block")))
                    comment_input_div.click()
                    time.sleep(random.uniform(0.2, 0.5))
                    comment_input_div.send_keys(comment_content)
                    print(f"📝 尝试发送第 {current_attempt} 条评论：'{comment_content}'")
                    time.sleep(random.uniform(0.3, 0.7))
                    comment_input_div.send_keys(Keys.ENTER)
                    print(f"✅ 第 {current_attempt} 条评论发送成功！当前成功数：{success_count + 1}/{comment_count}")
                    success_count += 1
                    current_attempt += 1
                    time.sleep(random.uniform(MIN_WAIT_TIME, MAX_WAIT_TIME))
                except Exception as e:
                    print(f"\n❌ 第 {current_attempt} 条评论发送失败：{e}")
                    print(f"⚠️  将休眠 {VERIFY_CODE_SLEEP_TIME} 秒后重试...")
                    for i in range(VERIFY_CODE_SLEEP_TIME, 0, -1):
                        print(f"⏳ 休眠倒计时：{i}秒", end="\r")
                        time.sleep(1)
                    print(f"\n🔄 重新尝试发送第 {current_attempt} 条评论...")

            # 4. 评论成功后更新数据库
            if success_count == comment_count:
                print(f"\n🎉 博主 {blogger_name} 的 {comment_count} 条评论已全部发送成功！")
                update_blogger_statistics(table_name, blogger_id, comment_count)

                # --- 新增调用 ---
                update_total_statistics(comment_count)

                processed = True
                return True

        except SkipBloggerException as e:
            print(f"ℹ️  {e}")
            return False
        except Exception as e:
            print(f"\n❌ 处理博主 {blogger_name} 时发生严重错误：{e}")
            print(f"⚠️  请确保已返回关注列表！将等待 {RETURN_LIST_WAIT_TIME} 秒后重试...")
            for i in range(RETURN_LIST_WAIT_TIME, 0, -1):
                print(f"⏳ 返回列表倒计时：{i}秒", end="\r")
                time.sleep(1)
            print(f"\n🔄 重新尝试处理博主 {blogger_name}...")
    return False


# -------------------------- 主程序 --------------------------
if __name__ == "__main__":
    # 初始化评论内容
    random_num_comments = [str(random.randint(1, 100)) for _ in range(100)]
    TASKS[0]['comment_contents'] = random_num_comments
    random_num_comments_group3 = [random.randint(1, 35) for _ in range(100)]
    TASKS[2]['comment_contents'] = random_num_comments_group3

    # 初始化浏览器
    edge_options = Options()
    edge_options.binary_location = EDGE_BINARY_PATH
    driver = None
    try:
        edge_service = Service(executable_path=EDGE_DRIVER_PATH)
        driver = webdriver.Edge(service=edge_service, options=edge_options)
        driver.maximize_window()
        wait = WebDriverWait(driver, 15)
        print("✅ Edge浏览器启动成功！")
    except Exception as e:
        print(f"\n❌ EdgeDriver初始化失败：{e}")
        exit()

    try:
        # 初始化本次运行的统计记录
        init_total_statistics()

        driver.get("https://www.douyin.com/")
        print("\n🌐 已打开抖音网页版，请手动登录（若未登录）...")
        time.sleep(5)

        print(f"\n⌛ 请在 {INITIAL_MANUAL_OPERATE_TIME} 秒内完成以下操作：")
        print("1. 登录抖音账号")
        print("2. 导航到你的「关注列表」页面")
        for i in range(INITIAL_MANUAL_OPERATE_TIME, 0, -1):
            print(f"⏳ 倒计时：{i}秒", end="\r")
            time.sleep(1)
        print("\n✅ 手动操作时间结束，开始执行评论任务...")

        # 遍历所有任务
        for task in TASKS:
            table_name = task['table_name']
            comment_templates = task['comment_contents']
            comment_count = task['comment_count']

            print(f"\n" + "=" * 60)
            print(f"📢 开始处理分组: {table_name}")
            print(f"📝 评论模板: {comment_templates[:5]}..." if len(
                comment_templates) > 5 else f"📝 评论模板: {comment_templates}")
            print(f"📊 计划为每个博主发送 {comment_count} 条评论")
            print("=" * 60)

            bloggers = get_bloggers_from_table(table_name)
            if not bloggers:
                print(f"ℹ️  表 {table_name} 中没有找到博主，跳过此分组。")
                continue

            print(f"✅ 从数据库中读取到 {len(bloggers)} 个博主。")
            for i, blogger in enumerate(bloggers):
                print(f"\n" + "-" * 50)
                print(f"🔹 正在处理第 {i + 1}/{len(bloggers)} 个博主：{blogger['name']}")
                print(f"ℹ️  累计发送天数: {blogger['send_days']}, 历史总发送: {blogger['total_send_count']} 条")
                print("-" * 50)

                success = send_comments_to_blogger(driver, wait, blogger, comment_templates, comment_count, table_name)

                if success and i < len(bloggers) - 1:
                    print(f"\n⌛ 请在 {RETURN_LIST_WAIT_TIME} 秒内手动返回到「关注列表」页面...")
                    for j in range(RETURN_LIST_WAIT_TIME, 0, -1):
                        print(f"⏳ 返回列表倒计时：{j}秒", end="\r")
                        time.sleep(1)
                    print(f"\n✅ 准备处理下一个博主...")

            print(f"\n🎉 分组 '{table_name}' 的所有博主处理完毕！")

        print("\n" + "=" * 50)
        print("📊 所有评论任务已完成！")
        print(f"📈 本次运行总计发送评论: {current_run_total} 条")
        if current_run_id:
            print(f"📋 本次统计记录ID: {current_run_id}")
        print("=" * 50)

    except KeyboardInterrupt:
        print("\n\n🛑 用户手动停止了脚本。")
        print(f"📊 截至停止前，本次运行已发送评论: {current_run_total} 条")
    except Exception as e:
        print(f"\n\n❌ 脚本执行发生未知异常：{e}")
        print(f"📊 截至异常发生前，本次运行已发送评论: {current_run_total} 条")
    finally:
        if driver:
            driver.quit()
            print("\n🔒 浏览器已关闭！")